// 2024.01.22 22:27 星期一 晴
/**
 * 本篇代码 旨在 根据 坐标点位 寻求 一个最大面积的 多边形
 * 1.0 寻求 最大多边形规则 根据对当前点位的 差值对比 寻找 离当前点位 最近的 就是 最优选择
 * 2.0 根据 高斯面积公式 (--鞋带面积公式) 求出 可能的 分支 多边形的 面积  返回 最大的 那个 多边形 的 坐标序列
 */
import { toStringTypeCheck } from '../../../components/aaanewApp/global/global';

//内部 关键 分割线
let splitPart = '_';

// 二维 坐标系
const xy = ['x', 'y'];

// 点位 不得重复
let uudata;

/**
 * 多边形 面积分支
 */
let branchInfo;

/**
 * 初始化 数据
 */
function initBigPolygon() {
  branchInfo = { data: [] };
  uudata = { sourceInfo: [], link: [] };
}

/**
 * 根据 输入的 坐标数组 求出 最大面积的 多边形
 * @param {Array} coordArr 二维数组 --  坐标数组
 */
function search_BigPolygon(coordArr) {
  initBigPolygon();

  let mapArr = mapcoordArr(coordArr);
  //   let key;
  let bigAreaArr;
  let bigAreaCoordObj = { data: [] };

  //   保留 初始值
  insideUUdata(mapArr);

  linkPolygon();
  //   console.log(coordArr);
  //   for (let i = 0; i < branchInfo['data'].length; i++) {
  // key = branchInfo['data'][i];
  // console.log(
  //   '索引值 : ' + branchInfo[key]['branchName'] + '\n',
  //   rebackCoordArr(branchInfo[key]['used'])
  // );
  //   }

  bigAreaArr = bigAreaFac();
  for (let j = 0; j < bigAreaArr.length; j++) {
    bigAreaCoordObj['data'].push(j);
    bigAreaCoordObj[j] = rebackCoordArr(branchInfo[bigAreaArr[j]]['used']);
    bigAreaCoordObj[j].push(bigAreaCoordObj[j][0]);
  }
  return bigAreaCoordObj;
}

/**
 * 返回 最大面积的 点位链表
 */
function bigAreaFac() {
  let key;
  let sortedArea = [];

  let bigArea = [];

  // 修订内容
  reviseBranchInfo();
  let currentArea;
  for (let i = 0; i < branchInfo['data'].length; i++) {
    key = branchInfo['data'][i];

    currentArea = Math.abs(
      xy_shoelace(rebackCoordArr(branchInfo[key]['used']))
    );
    branchInfo[key]['area'] = currentArea;
    sortArea(key);
  }
  //   console.log(sortedArea);

  function sortArea(str) {
    let locatIndex = 0;
    for (let i = sortedArea.length - 1; i >= 0; i--) {
      if (sortedArea[0] == undefined && sortedArea[i - 1] == undefined) {
        break;
      }
      if (branchInfo[sortedArea[i]]['area'] <= branchInfo[str]['area']) {
        locatIndex = i + 1;
        break;
      }
    }
    sortedArea.splice(locatIndex, 0, str);
  }

  bigArea[0] = sortedArea[sortedArea.length - 1];

  //   寻找 所有 最短的 绝对偏移量之和 的 节点
  for (let m = sortedArea.length - 2; m >= 0; m--) {
    if (bigArea[0] != sortedArea[m]) {
      break;
    } else {
      bigArea.push(sortedArea[m]);
    }
  }
  //   console.log(bigArea);
  return bigArea;
}

/**
 * 修订 branchInfo 修订 名称链接
 */
function reviseBranchInfo() {
  for (let i = 0; i < branchInfo['data'].length; i++) {
    let settleName = branchInfo['data'][i];
    let name = branchInfo[settleName]['branchName'];
    if (settleName != name) {
      branchInfo['data'].splice(i, 1, name);
      branchInfo[name] = branchInfo[settleName];
      delete branchInfo[settleName];
    }
  }
  //   console.log(branchInfo);
}

/**
 * 以分支 修正 完整 的 点位链接
 */
function linkPolygon() {
  let branch;
  //   初始 新创建 分支
  if (branchInfo['data'][0] == undefined) {
    // let used = [uudata['link'][0]];
    // let remain = [...uudata['link']];
    // remain.splice(0, 1);

    let minCoord_i = getMinCoord(uudata['sourceInfo']);
    let used = [uudata['link'][minCoord_i]];
    let remain = [...uudata['link']];
    remain.splice(minCoord_i, 1);

    increaseBranch('0', used, remain);
  }
  for (let i = 0; i < branchInfo['data'].length; i++) {
    branch = branchInfo[branchInfo['data'][i]];
    unitLinkPolygon(branch);
  }
  //   console.log(branchInfo);
}

/**
 * 将当前分支 补充完全 如果 有分支节点 创建 新分支
 * @param {Object} branch 分支
 */
function unitLinkPolygon(branch) {
  for (; branch['remain'].length > 0; ) {
    // 最后一个 使用过的 节点 坐标
    let a = uudata[branch['used'][branch['used'].length - 1]]['value'];

    let nearC = nearestCoord(a, rebackCoordArr(branch['remain']));
    //如果有多个 最近节点 创建 新分支  名称为 当前名 _ 分支第二个 节点名
    if (nearC.length > 1) {
      for (let i = 1; i < nearC.length; i++) {
        // 新生成 branchName
        let branchName = branch['branchName'];
        let nearCBranchName = nearC[i];
        let newbranch = generateBranch_data_str(branchName, nearCBranchName);

        // 计算 当前 使用的 所有节点
        let CurrentUsed = [...branch['used']];
        CurrentUsed.push(nearCBranchName);
        // 计算 当前 所剩余 节点
        let CurrentRemain = [...branch['remain']];
        let current_i = CurrentRemain.indexOf(nearCBranchName);
        CurrentRemain.splice(current_i, 1);

        // 创建新分支
        increaseBranch(newbranch, CurrentUsed, CurrentRemain);
      }
      branch['branchName'] = generateBranch_data_str(
        branch['branchName'],
        nearC[0]
      );
    }

    branch['used'].push(nearC[0]);
    let current_i = branch['remain'].indexOf(nearC[0]);
    branch['remain'].splice(current_i, 1);
  }
}

/**
 * 根据 引用的 坐标 变化 添加 分支
 * @param {String} pre 前缀
 * @param {String} fix 后缀
 */
function generateBranch_data_str(pre, fix) {
  let str;
  str = pre + splitPart + fix;
  return str;
}

/**
 * 找出 所有 最近的节点 返回 Array
 * @param {Array} current 当前 坐标节点
 * @param {Array} remainArr 剩余 坐标节点
 */
function nearestCoord(current, remainArr) {
  //所有最近的节点
  let nearCoord = [];

  // 循环 节点
  let coordX;

  // 绝对偏移量 X
  let abx;

  // 绝对偏移量 Y
  let aby;

  // 所有的 绝对偏移量 之和
  let abxyArr = { data: [], sorted: [] };

  let key;

  //   计算 所有节点的 绝对 偏移量
  for (let i = 0; i < remainArr.length; i++) {
    coordX = remainArr[i];
    abx = Math.abs(coordX[0] - current[0]);
    aby = Math.abs(coordX[1] - current[1]);

    key = uudata['link'][getUUid_i(coordX)];
    abxyArr['data'].push(key);
    abxyArr[key] = abx * abx + aby * aby;
  }
  // 排序 偏移量
  for (let j = 0; j < abxyArr['data'].length; j++) {
    sortabxy(abxyArr['data'][j]);
  }
  //   console.log(
  //     '当前 找寻 到 的 剩余节点 绝对偏移量之和 及 排序后的 数据:',
  //     abxyArr
  //   );
  nearCoord[0] = abxyArr['sorted'][0];

  //   寻找 所有 最短的 绝对偏移量之和 的 节点
  for (let m = 1; m < abxyArr['sorted'].length; m++) {
    if (abxyArr[nearCoord[0]] != abxyArr[abxyArr['sorted'][m]]) {
      break;
    } else {
      nearCoord.push(abxyArr['sorted'][m]);
    }
  }

  function sortabxy(str) {
    let locatIndex = 0;
    for (let i = abxyArr['sorted'].length; i >= 0; i--) {
      if (
        abxyArr['sorted'][0] == undefined &&
        abxyArr['sorted'][i - 1] == undefined
      ) {
        break;
      }
      if (abxyArr[abxyArr['sorted'][i]] <= abxyArr[str]) {
        locatIndex = i + 1;
        break;
      }
    }
    abxyArr['sorted'].splice(locatIndex, 0, str);
  }
  //   console.log(nearCoord);
  return nearCoord;
}

/**
 * 创建分支
 * @param {String} branchName 分支名称
 * @param {Array} used 使用 过的 所有节点
 * @param {Array} remain 未使用的 剩余 节点
 */
function increaseBranch(branchName, used, remain) {
  branchInfo['data'].push(branchName);
  branchInfo[branchName] = { branchName, used, remain };
}

/**
 * 创建 领域
 * @param {Array} arr 唯一 坐标节点
 */
function insideUUdata(arr) {
  if (toStringTypeCheck(arr, 'array')) {
    uudata['sourceInfo'] = [...arr];
  }
  for (let i = 0; i < arr.length; i++) {
    increaseUUid(i);
  }
  function increaseUUid(r) {
    let uuid = crypto.randomUUID().toString();
    uuid = uuid.split('').splice(0, 6).join('');
    // console.log(uuid);

    uudata['link'].push(uuid);
    uudata[uuid] = { value: arr[r] };
  }
}

/**
 * 确立点位 唯一性 返回 一个 新的 点位数组
 * @param {Array} arr 点位 数组
 * @returns {Array} 无重复 的 点位数组
 */
function mapcoordArr(arr) {
  let a = [];

  for (let i = 0; i < arr.length; i++) {
    if (getindex(arr[i]) > -1) {
      continue;
    } else {
      a.push([...arr[i]]);
    }
  }
  function getindex(sma) {
    for (let j = 0; j < a.length; j++) {
      if (a[j][0] == sma[0] && a[j][1] == sma[1]) {
        return j;
      }
    }
    return -1;
  }

  return a;
}

/**
 * 获取 唯一标识码的 index
 * @param {String} str uuid 唯一标识码 || 坐标节点
 */
function getUUid_i(str) {
  if (toStringTypeCheck(str, 'string')) {
    return uudata['link'].indexOf(str);
  } else if (toStringTypeCheck(str, 'array')) {
    for (let i = 0; i < uudata['sourceInfo'].length; i++) {
      if (
        uudata['sourceInfo'][i][0] == str[0] &&
        uudata['sourceInfo'][i][1] == str[1]
      ) {
        return i;
      }
    }
  }
  return -1;
}

function getMinCoord(polygonArr) {
  let limtX = polygonArr[0][0];
  let limtY = polygonArr[0][1];
  let ind = 0;
  for (let i = 1; i < polygonArr.length; i++) {
    if (limtX > polygonArr[i][0]) {
      limtX = polygonArr[i][0];
      limtY = polygonArr[i][1];
      ind = i;
    } else if ((limtX = polygonArr[i][0])) {
      if (limtY > polygonArr[i][1]) {
        limtY = polygonArr[i][1];
        ind = i;
      }
    }
  }
  return ind;
}

/**
 * 将点位名称 数组 转化成点位数组
 * @param {Array} uidArr uid 的 点位名称 数组
 * @returns
 */
function rebackCoordArr(uidArr) {
  let a = [];
  for (let i = 0; i < uidArr.length; i++) {
    a.push(uudata[uidArr[i]]['value']);
  }
  return a;
}

/**
 * 根据二维 点位坐标 进行 原点位 面积计算
 * @param {Array} xyArr 多边形 二维点坐标
 */
function xy_shoelace(xyArr) {
  let extendXYArr = mapcoordArr(xyArr);
  extendXYArr.push(xyArr[0]);

  let countArea = 0;
  let currentArea = 0;
  for (let i = 0; i < extendXYArr.length - 1; i++) {
    currentArea =
      extendXYArr[i][0] * extendXYArr[i + 1][1] -
      extendXYArr[i][1] * extendXYArr[i + 1][0];
    countArea += currentArea;
  }
  countArea = countArea / 2;
  //   console.log(countArea);
  return countArea;
}

/**
 * 判断 点位坐标是否在 多边形 内 或 多边形上  返回 {inthePolygon: true,settle:[in,on,point],point:[]}
 * @param {Array} xy 二维点位坐标
 * @param {Array} polygonArr 二维 多边形 点位坐标 (闭合)
 */
function xy_inPolygon(xy, polygonArr) {
  let key;
  let returnInfo = { inthePolygon: false, settle: [] };
  let countArr = [];

  //   判断节点 是否在 该多边形最大 矩形区域内
  let bigAreaLine = getPolygonBigAreaLine(polygonArr);
  if (!xy_overBigAreaLine(xy, bigAreaLine)) {
    return returnInfo;
  }

  // 判断 是否在点位上
  for (let i = 0; i < polygonArr.length; i++) {
    key = polygonArr[i];
    if (xy[0] == key[0] && xy[1] == key[1]) {
      returnInfo['inthePolygon'] = true;
      returnInfo['settle'].push('point');
      returnInfo['point'] = key;
      return returnInfo;
    }
  }
  // 判断 是否在 线段上 利用向量的叉积 进行计算 是否为零
  for (let i = 0; i < polygonArr.length - 1; i++) {
    key = polygonArr[i];
    let crossProduct;
    let vectorA = { x: xy[0] - key[0], y: xy[1] - key[1] };
    let vectorB = {
      x: polygonArr[i + 1][0] - key[0],
      y: polygonArr[i + 1][1] - key[1],
    };
    //叉积 计算 -- 所构成的 平行四边形 面积
    crossProduct = vectorA.x * vectorB.y - vectorA.y * vectorB.x;

    if (crossProduct == 0) {
      // 需要 给定点位 在线段 区域内 简单判断 在 i 和 i+1 的 x 中间
      if (
        (xy[0] < key[0] || xy[0] < polygonArr[i + 1][0]) &&
        (xy[0] > key[0] || xy[0] > polygonArr[i + 1][0])
      ) {
        returnInfo['inthePolygon'] = true;
        returnInfo['settle'].push('on');
        return returnInfo;
      }
    }
  }

  // 判断 点位的x正向射线 与多边形 边 的 交集点位 个数 单数在内部 true
  for (let i = 0; i < polygonArr.length - 1; i++) {
    key = polygonArr[i];
    let key1 = polygonArr[i + 1];

    // 需要 给定点位 在 线段 区域内 ([i,i+1].x 至少 有一个 大于 xy.x) && ([i,i+1].y 需要一个大于 一个小于 xy.y)
    // if (key[0] >= xy[0] || polygonArr[i + 1][0] >= xy[0]) {
    //   if (
    //     (xy[1] <= key[1] || xy[1] <= polygonArr[i + 1][1]) &&
    //     (xy[1] >= key[1] || xy[1] >= polygonArr[i + 1][1])
    //   ) {

    if (xy_parallelBigAreaLineForY(xy, [key, key1])) {
      let vectorA = { x: 0, y: xy[1] - key[1] };
      let vectorB = {
        x: polygonArr[i + 1][0] - key[0],
        y: polygonArr[i + 1][1] - key[1],
      };
      vectorA.x = (vectorA.y * vectorB.x) / vectorB.y;
      if (vectorA.x + key[0] > xy[0]) {
        countArr.push([vectorA.x + key[0], vectorA.y + key[1]]);
      }
    }
  }
  //   删除重复 节点位
  countArr = mapcoordArr(countArr);

  //   判断 无重复交集点个数
  if (countArr.length % 2 == 1) {
    returnInfo['inthePolygon'] = true;
    returnInfo['settle'].push('in');
    // console.log(countArr);
  }

  return returnInfo;
}

/**
 * 给定 两条线段 判定是否相交
 * @param {Array} line1 二维 两个点位 数据  代表 一条线
 * @param {Array} line2 二维 两个点位 数据  代表 一条线
 * @returns -- {boolean} 两条线段 是否相交
 */
function line_intersectLine(line1, line2) {
  //   console.log(line1, line2);
  const [a, b] = line1;
  const [c, d] = line2;

  const d1 = crossProduct(a, b, c);
  const d2 = crossProduct(a, b, d);
  const d3 = crossProduct(c, d, a);
  const d4 = crossProduct(c, d, b);

  if (d1 * d2 < 0 && d3 * d4 < 0) {
    return true;
  }
  if (d1 == 0 && onSegment(c, line1)) return true;
  if (d2 == 0 && onSegment(d, line1)) return true;
  if (d3 == 0 && onSegment(a, line2)) return true;
  if (d4 == 0 && onSegment(b, line2)) return true;

  return false;
}

/**
 * 根据 起始点位 和 转向点位 所构成的 两个 向量 计算 叉积
 * @param {Array} p1 固定点位
 * @param {Array} p2 起始 点位
 * @param {Array} p3 转向 点位
 * @returns -- {Number} 返回 自然数
 */
function crossProduct(p1, p2, p3) {
  const x1 = p2[0] - p1[0];
  const y1 = p2[1] - p1[1];
  const x2 = p3[0] - p1[0];
  const y2 = p3[1] - p1[1];
  return x1 * y2 - x2 * y1;
}

/**
 * 判定 一个点位 是否在 线段所在的 矩形区域内
 * @param {Array} p 二维 点位  单个点位
 * @param {Array} line 二维 两个点位 数据  代表 一条线
 * @returns --{Boolean} 返回 是否 该点位在 线段的矩形区域内 (在矩形区域内 和 矢量叉积为零 判定 是否在线段上)
 */
function onSegment(p, line) {
  const [a, b] = line;
  const [x, y] = p;
  return (
    x >= Math.min(a[0], b[0]) &&
    x <= Math.max(a[0], b[0]) &&
    y >= Math.min(a[1], b[1]) &&
    y <= Math.max(a[1], b[1])
  );
}

/**
 * 判断 两个 多边形 是否 相交
 * @param {Array} polygon1 多边形 节点 二维 节点数据
 * @param {Array} polygon2 多边形 节点 二维 节点数据
 */
function polygon_intersectPolygon(polygon1, polygon2) {
  let isIntersect = false;
  let coord_i;
  polygon1 = mapcoordArr(polygon1);
  polygon2 = mapcoordArr(polygon2);

  let line1;
  let line2;

  //   检测 两个 多边形 是否 有碰撞体积 没有 直接返回 false
  let isOver = false;

  //   获取 多边形 最大 的 矩形域 的对角线
  //   let temp1 = getPolygonBigAreaLine(polygon1);
  //   获取 多边形 最大 的 矩形域 的对角线
  let temp2 = getPolygonBigAreaLine(polygon2);
  //   检测 最大域 对角点位 是否 over
  for (let i = 0; i < polygon1.length; i++) {
    if (xy_overBigAreaLine(polygon1[i], temp2)) {
      isOver = true;
    }
  }

  if (isOver) {
    return true;
  }

  // 检测 两个 多边形的点位 是否在 另一个多边形内
  for (let i = 0; i < polygon1.length; i++) {
    coord_i = polygon1[i];
    isIntersect = xy_inPolygon(coord_i, polygon2)['inthePolygon'];
    if (isIntersect) {
      return isIntersect;
    }
  }
  for (let i = 0; i < polygon2.length; i++) {
    coord_i = polygon2[i];
    isIntersect = xy_inPolygon(coord_i, polygon1)['inthePolygon'];
    if (isIntersect) {
      return isIntersect;
    }
  }

  //闭合 多边形
  polygon1.push(polygon1[0]);
  polygon2.push(polygon2[0]);

  for (let i = 0; i < polygon1.length - 1; i++) {
    line1 = [polygon1[i], polygon1[i + 1]];
    for (let j = 0; j < polygon2.length - 1; j++) {
      line2 = [polygon2[j], polygon2[j + 1]];
      isIntersect = line_intersectLine(line1, line2);
      if (isIntersect) {
        return isIntersect;
      }
    }
  }

  return false;
}

/**
 * 判断点位 是否在 对角线 矩形区域内
 * @param {Array} xy 二位点位
 * @param {Array} bigLine 元素 最大矩形域 对角线 二位点位
 */
function xy_overBigAreaLine(xy, bigLine) {
  const [x, y] = xy;
  const [a, b] = bigLine;
  return (
    x >= Math.min(a[0], b[0]) &&
    x <= Math.max(a[0], b[0]) &&
    y >= Math.min(a[1], b[1]) &&
    y <= Math.max(a[1], b[1])
  );
}

/**
 * 判断点位 是否在 对角线 y 向区域内
 * @param {Array} xy 二位点位
 * @param {Array} bigLine 元素 最大矩形域 对角线 二位点位
 */
function xy_parallelBigAreaLineForY(xy, bigLine) {
  const [, y] = xy;
  const [a, b] = bigLine;
  return y >= Math.min(a[1], b[1]) && y <= Math.max(a[1], b[1]);
}

// /**
//  * 判断点位 是否在 对角线 x 向区域内
//  * @param {Array} xy 二位点位
//  * @param {Array} bigLine 元素 最大矩形域 对角线 二位点位
//  */
// function xy_parallelBigAreaLineForX(xy, bigLine) {
//   const [x,] = xy;
//   const [a, b] = bigLine;
//   return x >= Math.min(a[0], b[0]) && x <= Math.max(a[0], b[0]);
// }

/**
 * 根据点位 确立 最大包裹 矩形 返回 矩形的 对角线 点位数组
 * @param {Array} polygonArr 多边形点位
 */
function getPolygonBigAreaLine(polygonArr) {
  const left = getExtremum('min', 'x', polygonArr);
  const top = getExtremum('min', 'y', polygonArr);

  const right = getExtremum('max', 'x', polygonArr);
  const bottom = getExtremum('max', 'y', polygonArr);
  return [
    [left, top],
    [right, bottom],
  ];
}

/**
 * 根据多边形点位 确立 极限 max | min 的 x | y 值
 * @param {String} extremum max or min
 * @param {String} xOry x or y
 */
function getExtremum(extremum, xOry, polygonArr) {
  xOry = xy.indexOf(xOry);
  let returnNum = polygonArr[0][xOry];

  for (let i = 1; i < polygonArr.length; i++) {
    returnNum = Math[extremum](returnNum, polygonArr[i][xOry]);
  }
  return returnNum;
}
export {
  search_BigPolygon,
  xy_inPolygon,
  line_intersectLine,
  polygon_intersectPolygon,
};
